// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package rand

import (
	"errors"
	"io"
	"math/big"
)

// smallPrimes是一个小素数列表，它允许我们快速
// 搜索随机变量时排除部分组合候选项
// 首要的此列表在smallPrimesProduct超过
// a uint64。它不包括两个，因为我们确保候选人
// 奇怪的构造。
var smallPrimes = []uint8{
	3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
}

// smallPrimesProduct是smallPrimes中的值的乘积，允许我们
// 将候选素数减少这个数，然后确定它是否
// 小素数的所有元素的互质，无需进一步的big.Int
// 操作。
var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365)

// Prime返回给定大小的数字p，这样p就是Prime
// 可能性很大。
// Prime将为rand.Read或if bits<2返回的任何错误返回错误。
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
	if bits < 2 {
		err = errors.New("crypto/rand: prime size must be at least 2-bit")
		return
	}

	b := uint(bits % 8)
	if b == 0 {
		b = 8
	}

	bytes := make([]byte, (bits+7)/8)
	p = new(big.Int)

	bigMod := new(big.Int)

	for {
		_, err = io.ReadFull(rand, bytes)
		if err != nil {
			return nil, err
		}

		// 清除第一个字节中的位，以确保候选字节的大小<=位。
		bytes[0] &= uint8(int(1<<b) - 1)
		// 不要让值太小，即设置最重要的两位。
		// 设置前两位，而不仅仅是前两位，
		// 表示当两个值相乘时，
		// 结果一点也不差。
		if b >= 2 {
			bytes[0] |= 3 << (b - 2)
		} else {
			// 这里b==1，因为b不能为零。
			bytes[0] |= 1
			if len(bytes) > 1 {
				bytes[1] |= 0x80
			}
		}
		// 将值设为奇数，因为如此大的偶数肯定不是素数。
		bytes[len(bytes)-1] |= 1

		p.SetBytes(bytes)

		// 计算小素数乘积的mod值。如果是
		// 这些素数的倍数，我们加上两个，直到它不是。
		// 溢出的概率最小，可以忽略
		// 因为我们还在对结果进行米勒-拉宾测试。
		bigMod.Mod(p, smallPrimesProduct)
		mod := bigMod.Uint64()

	NextDelta:
		for delta := uint64(0); delta < 1<<20; delta += 2 {
			m := mod + delta
			for _, prime := range smallPrimes {
				if m%uint64(prime) == 0 && (bits > 6 || m != uint64(prime)) {
					continue NextDelta
				}
			}

			if delta > 0 {
				bigMod.SetUint64(delta)
				p.Add(p, bigMod)
			}
			break
		}

		// 有一个很小的可能性，通过添加delta，我们导致
		// 数字可能太长了一点。因此，我们检查比特伦
		// 在这里
		if p.ProbablyPrime(20) && p.BitLen() == bits {
			return
		}
	}
}

// Int在[0，max]中返回一个统一的随机值。如果max<=0，它将崩溃。
func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) {
	if max.Sign() <= 0 {
		panic("crypto/rand: argument to Int is <= 0")
	}
	n = new(big.Int)
	n.Sub(max, n.SetUint64(1))
	// bitLen是编码<max值所需的最大位长度。
	bitLen := n.BitLen()
	if bitLen == 0 {
		// 唯一有效的结果是0
		return
	}
	// k是编码小于max的值所需的最大字节长度。
	k := (bitLen + 7) / 8
	// b是max-1最高有效字节中的位数。
	b := uint(bitLen % 8)
	if b == 0 {
		b = 8
	}

	bytes := make([]byte, k)

	for {
		_, err = io.ReadFull(rand, bytes)
		if err != nil {
			return nil, err
		}

		// 清除第一个字节中的位以增加概率
		// 候选人是<max。
		bytes[0] &= uint8(int(1<<b) - 1)

		n.SetBytes(bytes)
		if n.Cmp(max) < 0 {
			return
		}
	}
}
